package data;

import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.el.ELContext;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import model.House;
import model.HouseList;
import model.Person;

import org.mindswap.pellet.*;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAssertionAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyAssertionAxiom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.reasoner.Node;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.vocab.PrefixOWLOntologyFormat;

import com.clarkparsia.pellet.owlapiv3.PelletReasoner;
import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory;

// make singleton?
public class GoTData {
	
	public static final IRI GoT_iri = IRI.create("http://www.semanticweb.org/dieter/ontologies/2013/4/untitled-ontology-13");

	public static final String BASE_URL = "http://www.semanticweb.org/dieter/ontologies/2013/4/untitled-ontology-13";

	private OWLOntologyManager manager;
	private OWLOntology ontology;
	private OWLDataFactory df;
	private PelletReasoner reasoner;
	private static GoTData _instance;
	private PrefixOWLOntologyFormat pm;
	
	public static GoTData Instance() {
		if (_instance == null) {
			try {
				_instance = new GoTData();
			} catch (OWLOntologyCreationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return _instance;
	}
	
	public GoTData() throws OWLOntologyCreationException{
		manager = OWLManager.createOWLOntologyManager();
		
		ExternalContext ext = FacesContext.getCurrentInstance().getExternalContext();
		String resourcesPath = ext.getRealPath("");
		
		ontology = manager.loadOntologyFromOntologyDocument(new File(resourcesPath+"/dataSource/dieterset_pellet.owl"));
		df = OWLManager.getOWLDataFactory();
		//PelletOptions.USE_UNIQUE_NAME_ASSUMPTION = true;
		reasoner = PelletReasonerFactory.getInstance().createReasoner( ontology );
		
		
		pm = (PrefixOWLOntologyFormat) manager.getOntologyFormat(ontology);
		pm.setDefaultPrefix(BASE_URL + "#");
	}
	
	public List<Person> getMembers(String houseName) {
		List<Person> members = new LinkedList<Person>();

		ExternalContext ext = FacesContext.getCurrentInstance().getExternalContext();
		String resourcesPath = ext.getRealPath("");

		//OWLObjectProperty op_memberOf = df
		//		.getOWLObjectProperty(IRI
		//				.create(GoT_iri + "#isMemberOfHouse"));
		OWLObjectProperty op_memberOf = df.getOWLObjectProperty(":hasMember", pm);
		
		OWLNamedIndividual house = df.getOWLNamedIndividual(IRI.create(GoT_iri + "#" + houseName));
		
		NodeSet<OWLNamedIndividual> in = reasoner.getObjectPropertyValues(
				house, op_memberOf);

		Iterator<Node<OWLNamedIndividual>> iIt = in.iterator();
		
		while (iIt.hasNext()) {
			Node<OWLNamedIndividual> indiv = iIt.next();
			
			Person person = new Person(indiv.getRepresentativeElement().getIRI()
					.getFragment());
			person.setAlive(isAlive(indiv.getRepresentativeElement()));
			
			members.add(person);
			
		}
		
		for (Person p:members) {
			System.out.println(p.getName());
		}
		
		return members;
	}
	
	public List<House> getHouses() {
		
		List<House> houses = new LinkedList<House>();
		
		OWLClass cls_House = df.getOWLClass(IRI.create("http://www.semanticweb.org/dieter/ontologies/2013/4/untitled-ontology-13#House"));
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getInstances(cls_House,false).iterator();
		
		ExternalContext ext = FacesContext.getCurrentInstance().getExternalContext();
		String resourcesPath = ext.getRealPath("");
		
		while (iIt.hasNext()) {
			OWLNamedIndividual indiv = iIt.next().getRepresentativeElement();
			
			houses.add(new House(indiv.getIRI().getFragment(),"/house_images/"+indiv.getIRI().getFragment()+".jpg"));
		}
		
		for (House h:houses) {
			System.out.println(h.getName());
		}
		
		return houses;
	}
	
	public OWLNamedIndividual getIndividual(String name) {
		return df.getOWLNamedIndividual(":" + name, pm);
	}
	
	public List<Person> getSiblings(OWLNamedIndividual person) {
		OWLObjectProperty op_hasSibling = df.getOWLObjectProperty(":hasSibling", pm);	
		return getObjectPropertyValues(person, op_hasSibling);
	}
	
	public List<Person> getChildren(OWLNamedIndividual person) {
		OWLObjectProperty op_hasChild = df.getOWLObjectProperty(":hasChild", pm);
		return getObjectPropertyValues(person, op_hasChild);
	}
	
	public List<Person> getHeirOf(OWLNamedIndividual person) {
		OWLObjectProperty op_isHeirOf = df.getOWLObjectProperty(":isHeirOf", pm);
		return getObjectPropertyValues(person, op_isHeirOf);
	}
	
	public Person getSpouse(OWLNamedIndividual person) {
		OWLObjectProperty op_hasSpouse = df.getOWLObjectProperty(":hasSpouse", pm);	
		return getFunctionalObjectPropertyValue(person, op_hasSpouse);
	}
	
	public Person getFather(OWLNamedIndividual person) {
		OWLObjectProperty op_hasFather = df.getOWLObjectProperty(":hasFather", pm);
		return getFunctionalObjectPropertyValue(person, op_hasFather);
	}
	
	public Person getMother(OWLNamedIndividual person) {
		OWLObjectProperty op_hasFather = df.getOWLObjectProperty(":hasMother", pm);
		return getFunctionalObjectPropertyValue(person, op_hasFather);
	}
	
	private Person getFunctionalObjectPropertyValue(OWLNamedIndividual person, OWLObjectProperty op) {
		List<Person> result = getObjectPropertyValues(person, op);
		if (!result.isEmpty()) {
			return result.get(0);
		} else {
			return null;
		}
	}
	
	private List<Person> getObjectPropertyValues(OWLNamedIndividual person, OWLObjectProperty op) {
		List<Person> result = new LinkedList<Person>();
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getObjectPropertyValues(person, op).iterator();
		
		while (iIt.hasNext()) {
			OWLNamedIndividual indiv = iIt.next().getRepresentativeElement();
			result.add(new Person(indiv.getIRI().getFragment()));
		}
		
		return result;
	}
	
	public List<String> getTitles(OWLNamedIndividual person) {
		OWLObjectProperty op_hasTitle = df.getOWLObjectProperty(":hasTitle", pm);
	
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getObjectPropertyValues(person, op_hasTitle).iterator();
		
		List<String> titles = new LinkedList<String>();
		
		while (iIt.hasNext()) {
			titles.add(iIt.next().getRepresentativeElement().getIRI().getFragment());
		}

		return titles;
	}
	
	public boolean isAlive(OWLNamedIndividual person) {
		OWLDataProperty dp_isAlive = df.getOWLDataProperty(":isAlive", pm);
		
		try {
			return person.getDataPropertyValues(dp_isAlive, ontology).iterator().next().parseBoolean();
		} catch (Throwable e) {
			// if data property isAlive not specified create with value true
			OWLDataPropertyAssertionAxiom dpax = df.getOWLDataPropertyAssertionAxiom(dp_isAlive, person, true);
			manager.applyChanges(manager.addAxiom(ontology, dpax));
			reasoner.refresh();
			return true;
		}
	}
	
	public int getYearOfBirth(OWLNamedIndividual person) {
		OWLDataProperty dp_DateOfBirth = df.getOWLDataProperty(":DateOfBirth", pm);
		
		try {
			return person.getDataPropertyValues(dp_DateOfBirth, ontology).iterator().next().parseInteger();
		} catch (Throwable e) {
			// unknown
			return -1;
		}
	}
	
	public String getInheritanceStatus(OWLNamedIndividual person) {
		OWLClassExpression cl_MayInherit = df.getOWLClass(":MayInherit", pm);
		OWLDataProperty dp_isAlive = df.getOWLDataProperty(":isAlive", pm);
		
		if (person.getDataPropertyValues(dp_isAlive, ontology).iterator().next().parseBoolean()) {
			if (reasoner.getInstances(cl_MayInherit, false).containsEntity(person)) {
				return "In line of succession";
			} else {
				return "Exluded from line of succession";
			}
		} else {
			return "";
		}
	}
	
	public void marry(String nameOne, String nameTwo) {
		marry(this.getIndividual(nameOne), this.getIndividual(nameTwo));
	}
	
	public void marry(OWLNamedIndividual indiv1,
			OWLNamedIndividual indiv2) {
		OWLObjectProperty op_hasSpouse = df.getOWLObjectProperty(":hasSpouse", pm);
		OWLAxiom axiom = df.getOWLObjectPropertyAssertionAxiom(op_hasSpouse,
				indiv1, indiv2);
		manager.applyChange(new AddAxiom(ontology, axiom));
		reasoner.refresh();
	}
	
	public void addTitle(String personstr, String titlestr) {
		OWLNamedIndividual person = this.getIndividual(personstr);
		OWLNamedIndividual title = this.getIndividual(titlestr);
		
		OWLClass cl_LoseInheritanceRigths = df.getOWLClass(
				":_LoseInheritanceRigths_", pm);
		OWLObjectProperty op_hasTitle = df
				.getOWLObjectProperty(":hasTitle", pm);
		
		Set<OWLIndividual> titles = person.getObjectPropertyValues(
				op_hasTitle, ontology);
		titles.iterator().next().getTypes(ontology).contains(cl_LoseInheritanceRigths);
		
		if (reasoner.getInstances(cl_LoseInheritanceRigths, false)
				.containsEntity(title) || titles.iterator().next().getTypes(ontology).contains(cl_LoseInheritanceRigths)
		 ) {
			// remove other titles from person
			Set<OWLAxiom> axioms = new HashSet<OWLAxiom>();
			for (OWLIndividual oldtitle : titles) {
				System.out.println("intitle");
				axioms.add(df.getOWLObjectPropertyAssertionAxiom(op_hasTitle,
						person, oldtitle));
			}
			manager.applyChanges(manager.removeAxioms(ontology, axioms));
		}
		// add aditional title
		manager.applyChanges(manager.addAxiom(ontology, df
				.getOWLObjectPropertyAssertionAxiom(op_hasTitle, person, title)));
		reasoner.refresh();
		System.out.println(person
				.getObjectPropertyValues(op_hasTitle, ontology));
	}

	
	public void switchPersonStatus(String name) {
		OWLNamedIndividual indiv = this.getIndividual(name);
		
		OWLDataProperty dp_isAlive = df.getOWLDataProperty(":isAlive", pm);

		System.out.println("-- isAlive status before: "
				+ indiv.getDataPropertyValues(dp_isAlive, ontology).toString());
		boolean oldstatus = indiv.getDataPropertyValues(dp_isAlive, ontology).iterator().next().parseBoolean();

		OWLAxiom oldaxiom = df.getOWLDataPropertyAssertionAxiom(dp_isAlive,
				indiv, oldstatus);
		OWLAxiom newaxiom = df.getOWLDataPropertyAssertionAxiom(dp_isAlive,
				indiv, !oldstatus);

		List<OWLOntologyChange> changes = new LinkedList<OWLOntologyChange>();
		changes.add(new RemoveAxiom(ontology, oldaxiom));
		changes.add(new AddAxiom(ontology, newaxiom));

		manager.applyChanges(changes);
		
		reasoner.refresh();
		
		System.out.println("-- isAlive status after: "
				+ indiv.getDataPropertyValues(dp_isAlive, ontology).toString());
	}
	
	public List<String> getWomen(){
		OWLClass cls = df.getOWLClass(":Woman", pm);
		List<String> women = new LinkedList<String>();
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getInstances(cls,
				false).iterator();
		System.out.print("-- Instances of Class" + cls.getIRI().getFragment()
				+ ":");
		while (iIt.hasNext()) {
			women.add(iIt.next().getRepresentativeElement().getIRI().getFragment());
		}
		return women;
	}
	
	public List<String> getMen(){
		OWLClass cls = df.getOWLClass(":Man", pm);
		List<String> men = new LinkedList<String>();
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getInstances(cls,
				false).iterator();
		System.out.print("-- Instances of Class" + cls.getIRI().getFragment()
				+ ":");
		while (iIt.hasNext()) {	
			men.add(iIt.next().getRepresentativeElement().getIRI().getFragment());
		}
		return men;
	}
	
	private List<String> getInstancesList(OWLClass cls) {
		List<String> result = new LinkedList<String>();
		Iterator<Node<OWLNamedIndividual>> iIt = reasoner.getInstances(cls,
				false).iterator();
		System.out.print("-- Instances of Class" + cls.getIRI().getFragment()
				+ ":");
		while (iIt.hasNext()) {	
			result.add(iIt.next().getRepresentativeElement().getIRI().getFragment());
		}
		return result;
	}
	
	public List<String> getPersonsList() {
		return getInstancesList(df.getOWLClass(":Person", pm));
	}
	
	public List<String> getTitlesList() {
		return getInstancesList(df.getOWLClass(":Title", pm));
	}
	
	public void addPerson(String name, int birthyear, String gender, String parent1_name, String parent2_name) {
		// create Individual
		OWLNamedIndividual parent1 = df
				.getOWLNamedIndividual(IRI.create(GoT_iri + "#" + parent1_name));

		OWLNamedIndividual parent2 = df
				.getOWLNamedIndividual(IRI.create(GoT_iri + "#" + parent2_name));
		
		OWLNamedIndividual indiv = df.getOWLNamedIndividual(IRI.create(GoT_iri + "#" + name));
		
		// add class Person and Gender
		OWLClass cl_person = df.getOWLClass(IRI.create(GoT_iri + "#Person"));
		OWLClassAssertionAxiom ax = df.getOWLClassAssertionAxiom(cl_person, indiv);
		
		/*String gender = "";
		if (male) {
			gender = "Man";
		} else {
			gender = "Woman";
		}*/
		
		OWLClass cl_gender = df.getOWLClass(IRI.create(GoT_iri + "#" + gender));
		OWLClassAssertionAxiom axgen = df.getOWLClassAssertionAxiom(cl_gender, indiv);
		
		List<OWLOntologyChange> changes = new LinkedList<OWLOntologyChange>();
		
		changes.addAll(manager.addAxiom(ontology, ax));
		changes.addAll(manager.addAxiom(ontology, axgen));
		
		// age?
		OWLDataProperty dp_DateOfBirth = df.getOWLDataProperty(IRI.create(GoT_iri + "#DateOfBirth"));
		OWLDataPropertyAssertionAxiom axdob = df.getOWLDataPropertyAssertionAxiom(dp_DateOfBirth, indiv, birthyear);

		changes.addAll(manager.addAxiom(ontology, axdob));
		
		// isAlive
		OWLDataProperty dp_isAlive = df.getOWLDataProperty(IRI.create(GoT_iri + "#isAlive"));
		OWLDataPropertyAssertionAxiom axal = df.getOWLDataPropertyAssertionAxiom(dp_isAlive, indiv, true);

		changes.addAll(manager.addAxiom(ontology, axal));
		
		// add parents
		OWLObjectProperty op_hasParent = df.getOWLObjectProperty(IRI.create(GoT_iri + "#hasParent"));
		OWLObjectPropertyAssertionAxiom axp1 = df.getOWLObjectPropertyAssertionAxiom(op_hasParent, indiv, parent1);
		OWLObjectPropertyAssertionAxiom axp2 = df.getOWLObjectPropertyAssertionAxiom(op_hasParent, indiv, parent2);
		
		changes.addAll(manager.addAxiom(ontology, axp1));
		changes.addAll(manager.addAxiom(ontology, axp2));
		
		manager.applyChanges(changes);
		// reason - check for brothers and set older brothers
		
		reasoner.refresh();
		
		OWLObjectProperty op_hasOlderSibling = df.getOWLObjectProperty(IRI.create(GoT_iri + "#hasOlderSibling"));
		NodeSet<OWLNamedIndividual> olderSiblings = reasoner.getObjectPropertyValues(indiv, op_hasOlderSibling);
		// if no older siblings assert is oldest sibling
		if (olderSiblings.isEmpty()) {
			// cardinality
			OWLClassExpression ce_os = df.getOWLObjectExactCardinality(0, op_hasOlderSibling);
			OWLClassAssertionAxiom axos = df.getOWLClassAssertionAxiom(ce_os, indiv);
			manager.applyChanges(manager.addAxiom(ontology, axos));		
		} else {
			OWLClassExpression ce_os = df.getOWLObjectExactCardinality(olderSiblings.getFlattened().size(), op_hasOlderSibling);
			OWLClassAssertionAxiom axos = df.getOWLClassAssertionAxiom(ce_os, indiv);
			manager.applyChanges(manager.addAxiom(ontology, axos));		
		}
		reasoner.refresh();
		reasoner.flush();
	}
	
		
}
